<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.2.0">


  <link rel="apple-touch-icon" sizes="180x180" href="/myblog/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/myblog/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/myblog/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/myblog/images/logo.svg" color="#222">

<link rel="stylesheet" href="/myblog/css/main.css">



<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.1/css/all.min.css">
  <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/animate.css@3.1.1/animate.min.css">

<script class="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"kuang-yudi.gitee.io","root":"/myblog/","images":"/myblog/images","scheme":"Gemini","version":"8.1.0","exturl":false,"sidebar":{"position":"right","display":"post","padding":18,"offset":12},"copycode":false,"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":"gitalk","storage":true,"lazyload":false,"nav":null,"activeClass":"gitalk"},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/myblog/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false}};
  </script>
<meta name="description" content="浏览器渲染页面过程">
<meta property="og:type" content="article">
<meta property="og:title" content="浏览器渲染页面过程">
<meta property="og:url" content="http://kuang-yudi.gitee.io/myblog/2021/01/12/%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B8%B2%E6%9F%93%E9%A1%B5%E9%9D%A2%E8%BF%87%E7%A8%8B/index.html">
<meta property="og:site_name" content="KY迪的个人博客">
<meta property="og:description" content="浏览器渲染页面过程">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://kuang-yudi.gitee.io/myblog/2021/01/12/%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B8%B2%E6%9F%93%E9%A1%B5%E9%9D%A2%E8%BF%87%E7%A8%8B/浏览器中的线程.jpg">
<meta property="og:image" content="http://kuang-yudi.gitee.io/myblog/2021/01/12/%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B8%B2%E6%9F%93%E9%A1%B5%E9%9D%A2%E8%BF%87%E7%A8%8B/浏览器渲染.jpg">
<meta property="og:image" content="http://kuang-yudi.gitee.io/myblog/2021/01/12/%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B8%B2%E6%9F%93%E9%A1%B5%E9%9D%A2%E8%BF%87%E7%A8%8B/js阻塞关键字.jpg">
<meta property="article:published_time" content="2021-01-12T06:52:53.000Z">
<meta property="article:modified_time" content="2021-02-24T11:40:48.289Z">
<meta property="article:author" content="匡宇迪">
<meta property="article:tag" content="回流重绘">
<meta property="article:tag" content="浏览器渲染过程">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://kuang-yudi.gitee.io/myblog/2021/01/12/%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B8%B2%E6%9F%93%E9%A1%B5%E9%9D%A2%E8%BF%87%E7%A8%8B/浏览器中的线程.jpg">


<link rel="canonical" href="http://kuang-yudi.gitee.io/myblog/2021/01/12/%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B8%B2%E6%9F%93%E9%A1%B5%E9%9D%A2%E8%BF%87%E7%A8%8B/">


<script class="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>
<title>浏览器渲染页面过程 | KY迪的个人博客</title>
  



  <noscript>
  <style>
  body { margin-top: 2rem; }

  .use-motion .menu-item,
  .use-motion .sidebar,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header {
    visibility: visible;
  }

  .use-motion .header,
  .use-motion .site-brand-container .toggle,
  .use-motion .footer { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle,
  .use-motion .custom-logo-image {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line {
    transform: scaleX(1);
  }

  .search-pop-overlay, .sidebar-nav { display: none; }
  .sidebar-panel { display: block; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/myblog/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">KY迪的个人博客</h1>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">个人技术博客</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu">
        <li class="menu-item menu-item-home"><a href="/myblog/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li>
        <li class="menu-item menu-item-tags"><a href="/myblog/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li>
        <li class="menu-item menu-item-categories"><a href="/myblog/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li>
        <li class="menu-item menu-item-archives"><a href="/myblog/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</div>
        
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <section class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%AD%E7%9A%84%E5%90%84%E4%B8%AA%E7%BA%BF%E7%A8%8B%E5%8F%8A%E4%BD%9C%E7%94%A8"><span class="nav-number">1.</span> <span class="nav-text">浏览器中的各个线程及作用</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-GUI-%E6%B8%B2%E6%9F%93%E7%BA%BF%E7%A8%8B"><span class="nav-number">1.1.</span> <span class="nav-text">1. GUI 渲染线程</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-JS-%E5%BC%95%E6%93%8E%E7%BA%BF%E7%A8%8B"><span class="nav-number">1.2.</span> <span class="nav-text">2. JS 引擎线程</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-%E4%BA%8B%E4%BB%B6%E8%A7%A6%E5%8F%91%E7%BA%BF%E7%A8%8B"><span class="nav-number">1.3.</span> <span class="nav-text">3. 事件触发线程</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-%E5%AE%9A%E6%97%B6%E8%A7%A6%E5%8F%91%E7%BA%BF%E7%A8%8B"><span class="nav-number">1.4.</span> <span class="nav-text">4. 定时触发线程</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-%E5%BC%82%E6%AD%A5-http-%E8%AF%B7%E6%B1%82%E7%BA%BF%E7%A8%8B"><span class="nav-number">1.5.</span> <span class="nav-text">5. 异步 http 请求线程</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E6%B5%8F%E8%A7%88%E5%99%A8%E9%A1%B5%E9%9D%A2%E6%B8%B2%E6%9F%93%E6%B5%81%E7%A8%8B"><span class="nav-number">2.</span> <span class="nav-text">浏览器页面渲染流程</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E8%A7%A3%E6%9E%90%E6%96%87%E6%A1%A3%E5%B9%B6%E6%9E%84%E5%BB%BA-DOM-%E6%A0%91-DOM-Tree"><span class="nav-number">2.1.</span> <span class="nav-text">1. 解析文档并构建 DOM 树(DOM Tree)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E6%9E%84%E5%BB%BA%E6%B8%B2%E6%9F%93%E6%A0%91-Render-Tree"><span class="nav-number">2.2.</span> <span class="nav-text">2. 构建渲染树(Render Tree)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-%E5%B8%83%E5%B1%80%E4%B8%8E%E7%BB%98%E5%88%B6%E6%B8%B2%E6%9F%93%E6%A0%91"><span class="nav-number">2.3.</span> <span class="nav-text">3. 布局与绘制渲染树</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-%E6%80%BB%E7%BB%93"><span class="nav-number">2.4.</span> <span class="nav-text">4. 总结</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%9B%9E%E6%B5%81-%E9%87%8D%E6%8E%92-%E5%92%8C%E9%87%8D%E7%BB%98"><span class="nav-number">3.</span> <span class="nav-text">回流(重排)和重绘</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-reflow-%E5%9B%9E%E6%B5%81"><span class="nav-number">3.1.</span> <span class="nav-text">1. reflow 回流</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-1-%E5%AE%9A%E4%B9%89"><span class="nav-number">3.1.1.</span> <span class="nav-text">1.1 定义</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-2-%E4%BA%A7%E7%94%9F"><span class="nav-number">3.1.2.</span> <span class="nav-text">1.2 产生</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-repaint-%E9%87%8D%E7%BB%98"><span class="nav-number">3.2.</span> <span class="nav-text">2. repaint 重绘</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-%E8%A7%A6%E5%8F%91-reflow-%E7%9A%84%E6%93%8D%E4%BD%9C"><span class="nav-number">3.3.</span> <span class="nav-text">3. 触发 reflow 的操作</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-%E5%87%8F%E5%B0%91-reflow-%E6%93%8D%E4%BD%9C"><span class="nav-number">3.3.1.</span> <span class="nav-text">3.1 减少 reflow 操作</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%85%B3%E9%94%AE%E6%B8%B2%E6%9F%93%E8%B7%AF%E5%BE%84-CRP-%E2%80%94%E2%80%94-Critical-Rendering-Path"><span class="nav-number">4.</span> <span class="nav-text">关键渲染路径 CRP —— Critical Rendering Path</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BC%98%E5%8C%96%E5%85%B3%E9%94%AE%E6%B8%B2%E6%9F%93%E8%B7%AF%E5%BE%84-%E5%8D%B3%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E4%B8%AD%E5%9C%A8%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B8%B2%E6%9F%93%E6%97%B6%E4%BC%98%E5%8C%96"><span class="nav-number">5.</span> <span class="nav-text">优化关键渲染路径(即性能优化中在浏览器渲染时优化)</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E7%BD%91%E7%BB%9C%E5%B1%82%E6%AC%A1%E6%96%B9%E9%9D%A2"><span class="nav-number">5.1.</span> <span class="nav-text">1 网络层次方面</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E8%B5%84%E6%BA%90%E4%BD%93%E7%A7%AF%E5%B1%82%E6%AC%A1%E6%96%B9%E9%9D%A2"><span class="nav-number">5.2.</span> <span class="nav-text">2 资源体积层次方面</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-HTML-%E6%96%87%E4%BB%B6%E7%BB%93%E6%9E%84%E5%B1%82%E6%AC%A1%E6%96%B9%E9%9D%A2"><span class="nav-number">5.3.</span> <span class="nav-text">3 HTML 文件结构层次方面</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-script-%E6%A0%87%E7%AD%BE%E5%B1%82%E6%AC%A1%E6%96%B9%E9%9D%A2"><span class="nav-number">5.4.</span> <span class="nav-text">4 script 标签层次方面</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-css-%E5%86%85%E5%AE%B9%E5%B1%82%E6%AC%A1%E6%96%B9%E9%9D%A2"><span class="nav-number">5.5.</span> <span class="nav-text">5 css 内容层次方面</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E9%98%BB%E5%A1%9E%E6%B8%B2%E6%9F%93"><span class="nav-number">6.</span> <span class="nav-text">阻塞渲染</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#js-%E9%98%BB%E5%A1%9E%E6%97%B6%E4%BD%BF%E7%94%A8-defer-%E4%B8%8E-async-%E5%85%B3%E9%94%AE%E5%AD%97"><span class="nav-number">7.</span> <span class="nav-text">js 阻塞时使用 defer 与 async 关键字</span></a></li></ol></div>
        </section>
        <!--/noindex-->

        <section class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="匡宇迪"
      src="https://static.pptstore.net/picture/19/19/e1feb44f42cdf7e1202b_s.jpg">
  <p class="site-author-name" itemprop="name">匡宇迪</p>
  <div class="site-description" itemprop="description">用于记录学习web前端</div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/myblog/archives/">
        
          <span class="site-state-item-count">38</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/myblog/categories/">
          
        <span class="site-state-item-count">7</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/myblog/tags/">
          
        <span class="site-state-item-count">87</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author site-overview-item animated">
      <span class="links-of-author-item">
        <a href="https://github.com/tonykyd/tonykyd.github.io" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;tonykyd&#x2F;tonykyd.github.io" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="mailto:1303665509@qq.com" title="E-Mail → mailto:1303665509@qq.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
  </div>



        </section>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://kuang-yudi.gitee.io/myblog/2021/01/12/%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B8%B2%E6%9F%93%E9%A1%B5%E9%9D%A2%E8%BF%87%E7%A8%8B/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="https://static.pptstore.net/picture/19/19/e1feb44f42cdf7e1202b_s.jpg">
      <meta itemprop="name" content="匡宇迪">
      <meta itemprop="description" content="用于记录学习web前端">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="KY迪的个人博客">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          浏览器渲染页面过程
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-01-12 14:52:53" itemprop="dateCreated datePublished" datetime="2021-01-12T14:52:53+08:00">2021-01-12</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">更新于</span>
        <time title="修改时间：2021-02-24 19:40:48" itemprop="dateModified" datetime="2021-02-24T19:40:48+08:00">2021-02-24</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/myblog/categories/%E9%9D%A2%E8%AF%95%E9%A2%98/" itemprop="url" rel="index"><span itemprop="name">面试题</span></a>
        </span>
    </span>

  
    <span class="post-meta-item" title="阅读次数" id="busuanzi_container_page_pv" style="display: none;">
      <span class="post-meta-item-icon">
        <i class="far fa-eye"></i>
      </span>
      <span class="post-meta-item-text">阅读次数：</span>
      <span id="busuanzi_value_page_pv"></span>
    </span>
</div>

            <div class="post-description">浏览器渲染页面过程</div>
        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <h1 id="浏览器中的各个线程及作用"><a href="#浏览器中的各个线程及作用" class="headerlink" title="浏览器中的各个线程及作用"></a>浏览器中的各个线程及作用</h1><img src="浏览器中的线程.jpg" style="width:30%"/>

<h2 id="1-GUI-渲染线程"><a href="#1-GUI-渲染线程" class="headerlink" title="1. GUI 渲染线程"></a>1. GUI 渲染线程</h2><p>1、负责渲染浏览器界面。解析 HTML、CSS，构建 DOM、CSSOM，布局和绘制等。<br>2、当页面需要重绘或者回流（重排）时，此线程将会被执行。<br>3、GUI 线程与 JS 引擎线程是互斥。当 JS 引擎执行时，GUI 线程就会被挂起，只有 JS 引擎空闲时，GUI 线程才能被执行。</p>
<h2 id="2-JS-引擎线程"><a href="#2-JS-引擎线程" class="headerlink" title="2. JS 引擎线程"></a>2. JS 引擎线程</h2><p>1、JavaScript 引擎，也称为 JS 内核，负责处理 JavaScript 脚本程序。<br>2、JS 引擎线程负责解析 JavaScript 脚本，运行代码。<br>3、JS 引擎与 GUI 线程是互斥。</p>
<h2 id="3-事件触发线程"><a href="#3-事件触发线程" class="headerlink" title="3. 事件触发线程"></a>3. 事件触发线程</h2><p>1、归属于浏览器而不是 JS 引擎，用来控制事件循环。</p>
<h2 id="4-定时触发线程"><a href="#4-定时触发线程" class="headerlink" title="4. 定时触发线程"></a>4. 定时触发线程</h2><p>1、setInterval 和 setTimeout 所在线程。<br>2、对于计数异步函数，不会由 JS 引擎计数（因为 JS 引擎是单线程，如果 JS 执行脚本过长导致阻塞便会计数不准，因此使用一单独线程用于 setInterval 和 setTimeout 等计数异步函数）。<br>3、注意：setTimeout 最低时间间隔为 4ms，也就是说当时间间隔低于 4ms，便以 4ms 间隔计数。</p>
<h2 id="5-异步-http-请求线程"><a href="#5-异步-http-请求线程" class="headerlink" title="5. 异步 http 请求线程"></a>5. 异步 http 请求线程</h2><p>用于 http 请求处理。</p>
<h1 id="浏览器页面渲染流程"><a href="#浏览器页面渲染流程" class="headerlink" title="浏览器页面渲染流程"></a>浏览器页面渲染流程</h1><p>如图：<br><img src="浏览器渲染.jpg"/></p>
<h2 id="1-解析文档并构建-DOM-树-DOM-Tree"><a href="#1-解析文档并构建-DOM-树-DOM-Tree" class="headerlink" title="1. 解析文档并构建 DOM 树(DOM Tree)"></a>1. 解析文档并构建 DOM 树(DOM Tree)</h2><p>解析内容可以分为三个部分：(webkit 内核)</p>
<ul>
<li>HTML/XHTML/SVG：解析这三种文件后，会生成 DOM 树（DOM Tree）</li>
<li>CSS：解析样式表，生成 CSS 规则树（CSS Rule Tree）/CSS OM 对象模型</li>
<li>JavaScript：解析脚本，通过 DOM API 和 CSSOM API 操作 DOM Tree 和 CSS Rule Tree，与用户进行交互。</li>
</ul>
<p>以上三类文件的执行顺序会根据其在文档中的位置及其标签属性的不同而有异同，具体在后文进行讨论。</p>
<h2 id="2-构建渲染树-Render-Tree"><a href="#2-构建渲染树-Render-Tree" class="headerlink" title="2. 构建渲染树(Render Tree)"></a>2. 构建渲染树(Render Tree)</h2><p>解析文档完成后，<strong>浏览器引擎</strong>会将 CSS Rule Tree 附着到 DOM Tree 上，并根据 DOM Tree 和 CSS Rule Tree 构造 Rendering Tree（渲染树）。Render 树主要是用于显示渲染。<br>Render 树的每一个节点我们叫它渲染器 renderer。</p>
<p>此处需要注意：</p>
<ul>
<li>Render Tree 和 DOM Tree 的区别在于，类似 Head 或 display：none 不会放在渲染树中；</li>
<li>将 CSS Rule Tree 匹配到 DOM Tree 需要解析 CSS 的选择器，为了提高该过程的性能，DOM 树应该尽量小，CSS Selector 应该尽量使用 id 和 class，避免过度层叠。</li>
</ul>
<h2 id="3-布局与绘制渲染树"><a href="#3-布局与绘制渲染树" class="headerlink" title="3. 布局与绘制渲染树"></a>3. 布局与绘制渲染树</h2><p><strong>布局 render tree</strong>：通过解析 position, overflow, z-index 等等影响布局的相关属性计算每一个渲染树节点的位置和大小并输出 box 盒模型，盒模型精确表示了每一个元素的位置和大小，并且所有相对度量单位此时都转化为了绝对单位。此过程被称为布局。</p>
<p><strong>绘制 render tree</strong>：渲染引擎会遍历 Render 树，并调用 renderer 的 paint() 方法，将 renderer 的内容显示在屏幕上。绘制工作是使用 UI 后端组件完成的。</p>
<p>注意：<br>渲染树的节点，在 Gecko 中称为 frame，而在 webkit 中称为 renderer；</p>
<hr>
<h2 id="4-总结"><a href="#4-总结" class="headerlink" title="4. 总结"></a>4. 总结</h2><p>浏览器从服务器获取文档并从上到下进行解析，在脚本文件标签不包含 defer 和 async 属性的情况下，会按照如下规则执行文档内容：</p>
<p>4.1 解析 html 文档，遇到 HTML 标签时，构建 DOM 树<br>4.2 在构建 DOM 的过程中，如果遇到外联的样式声明或脚本声明，则暂停文档解析，创建新的网络连接，开始下载样式文件和脚本文件<br>4.3 样式文件下载完成后，构建 CSS Rule DOM，脚本文件下载完成后，解释并立即执行。<br>4.4 构建 DOM 的同时，结合 CSS 规则树完成页面渲染。<br>4.5 如果 DOM 树先于 CSS 规则树构建完成，则在 CSS 规则树构建完成后，页面会发生一次重绘，将新构建的 CSS 规则应用于渲染树。</p>
<p><strong>注：解释 4.2 中脚本和样式文件对页面渲染的影响：</strong></p>
<p>在这个过程中，脚本文件的下载和执行是与文档解析同步进行的，脚本文件的下载和执行都会阻塞文档的解析，如果控制得不好，在用户体验上就会造成一定程度的影响。</p>
<h1 id="回流-重排-和重绘"><a href="#回流-重排-和重绘" class="headerlink" title="回流(重排)和重绘"></a>回流(重排)和重绘</h1><h2 id="1-reflow-回流"><a href="#1-reflow-回流" class="headerlink" title="1. reflow 回流"></a>1. reflow 回流</h2><h3 id="1-1-定义"><a href="#1-1-定义" class="headerlink" title="1.1 定义"></a>1.1 定义</h3><p>页面上某些元素的占位面积、定位方式、边距等属性的变化，都会引起它内部、周围甚至整个页面的重新渲染。浏览器<strong>根据各样式计算元素的尺寸和位置，构建渲染树</strong>的过程称之为 reflow。</p>
<h3 id="1-2-产生"><a href="#1-2-产生" class="headerlink" title="1.2 产生"></a>1.2 产生</h3><p>当渲染树节点的<strong>尺寸，布局，隐藏等影响布局的属性发生改变</strong>时，会<strong>触发 reflow</strong>操作，重新构建 Render Tree。每个页面在第一次加载的时候，会发生一次回流。</p>
<p>完成回流后，浏览器会重新绘制受影响的部分到屏幕中，因此，回流必定会导致重绘。</p>
<h2 id="2-repaint-重绘"><a href="#2-repaint-重绘" class="headerlink" title="2. repaint 重绘"></a>2. repaint 重绘</h2><p>改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时，屏幕的一部分要重画，但是元素的几何尺寸没有变。</p>
<h2 id="3-触发-reflow-的操作"><a href="#3-触发-reflow-的操作" class="headerlink" title="3. 触发 reflow 的操作"></a>3. 触发 reflow 的操作</h2><ul>
<li>增加、删除、修改 DOM 结点</li>
<li>移动 DOM 的位置</li>
<li>绘制动画</li>
<li>修改 CSS 样式</li>
<li>Resize 窗口的时候或是滚动</li>
<li>修改网页的默认字体</li>
</ul>
<p>Reflow 的成本比 Repaint 的成本高得多。DOM Tree 里的每个结点都会有 reflow 方法，一个结点的 reflow 很有可能导致子结点，甚至父点以及同级结点的 reflow。在一些高性能的电脑上也许还没什么，但是如果 reflow 发生在手机上，那么这个过程是非常痛苦和耗电的。所以，下面这些动作会触发 reflow 操作，有很大可能会是成本比较高的。</p>
<p><strong>注意：</strong></p>
<ul>
<li><p>display:none 会触发 reflow，而 visibility:hidden 只会触发 repaint，因为没有发生位置变化。</p>
</li>
<li><p>有些情况下，比如修改了元素的样式，浏览器并不会立刻 reflow 或 repaint 一次，而是会把这样的操作积攒一批，然后做一次 reflow，这又叫异步 reflow 或增量异步 reflow。</p>
</li>
<li><p>有些情况下，比如 resize 窗口，改变了页面默认的字体等。对于这些操作，浏览器会马上进行 reflow。</p>
</li>
</ul>
<h3 id="3-1-减少-reflow-操作"><a href="#3-1-减少-reflow-操作" class="headerlink" title="3.1 减少 reflow 操作"></a>3.1 减少 reflow 操作</h3><p>减少回流和重绘其实就是需要减少对 render tree 的操作（合并多次多 DOM 和样式的修改），并减少对一些样式信息的请求，尽量利用好浏览器的优化策略。</p>
<p>① 让要操作的元素进行”离线处理”，处理完后一起更新，这里所谓的”离线处理”即让元素不存在于 render tree 中，比如：</p>
<p>a) 使用 documentFragment 或 div 等元素进行缓存操作，这个主要用于添加元素的时候，就是先把所有要添加到元素添加到 1 个 div(这个 div 也是新加的)，<br>最后才把这个 div append 到 body 中。<br>b) 先 display:none 隐藏元素，然后对该元素进行所有的操作，最后再显示该元素。因为设置 display:none 的元素不在渲染树中，进行操作不会引起回流、重绘。所以只要操作只会有 2 次回流。</p>
<p>② 减少使用 table 布局 —— 千万不要使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局(回流重绘)</p>
<p>③ 通过切换定义好的类代替直接更改 CSS 样式(直接改变 className)，如果动态改变样式，则使用 cssText 一次性设置元素的所有属性（考虑没有优化的浏览器）</p>
<p>④ 将需要多次重排的元素，position 属性设为 absolute 或 fixed，这样此元素就脱离了文档流，它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位。</p>
<p>⑤ 让元素脱离动画流，减少回流的 Render Tree 的规模</p>
<p>⑥ 避免重复修改属性，将属性放在变量中，操作变量。例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F; 不推荐，重复回流</span><br><span class="line">for(循环) &#123;</span><br><span class="line">    elel.style.left &#x3D; el.offsetLeft + 5 + &quot;px&quot;;</span><br><span class="line">    elel.style.top  &#x3D; el.offsetTop  + 5 + &quot;px&quot;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F; 这样写好点</span><br><span class="line">var left &#x3D; el.offsetLeft,top  &#x3D; el.offsetTop,s &#x3D; el.style;</span><br><span class="line">for(循环) &#123;</span><br><span class="line">    left +&#x3D; 10;</span><br><span class="line">    top  +&#x3D; 10;</span><br><span class="line">    s.left &#x3D; left + &quot;px&quot;;</span><br><span class="line">    s.top  &#x3D; top  + &quot;px&quot;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="关键渲染路径-CRP-——-Critical-Rendering-Path"><a href="#关键渲染路径-CRP-——-Critical-Rendering-Path" class="headerlink" title="关键渲染路径 CRP —— Critical Rendering Path"></a>关键渲染路径 CRP —— Critical Rendering Path</h1><p>关键渲染路径是浏览器将 HTML、CSS、JavaScript 转换为屏幕上呈现的像素内容所经历的一系列步骤。也就是我们上面说的浏览器渲染流程。</p>
<h1 id="优化关键渲染路径-即性能优化中在浏览器渲染时优化"><a href="#优化关键渲染路径-即性能优化中在浏览器渲染时优化" class="headerlink" title="优化关键渲染路径(即性能优化中在浏览器渲染时优化)"></a>优化关键渲染路径(即性能优化中在浏览器渲染时优化)</h1><h2 id="1-网络层次方面"><a href="#1-网络层次方面" class="headerlink" title="1 网络层次方面"></a>1 网络层次方面</h2><p>1、要求服务器使用 GZIP 等压缩响应报文方式发送请求<br>2、使用 HTTP 缓存，减少在服务器上资源未改变时，资源请求的浪费<br>3、使用长连接（keep-alive）方式，减少由于 TCP 频繁建立连接所带来的的资源及时间消耗</p>
<h2 id="2-资源体积层次方面"><a href="#2-资源体积层次方面" class="headerlink" title="2 资源体积层次方面"></a>2 资源体积层次方面</h2><p>1、对相关图片在不失真的前提下进行压缩<br>2、使用 icon 替换小图标<br>3、webpack 项目整体的代码压缩</p>
<h2 id="3-HTML-文件结构层次方面"><a href="#3-HTML-文件结构层次方面" class="headerlink" title="3 HTML 文件结构层次方面"></a>3 HTML 文件结构层次方面</h2><p>1、样式标签 style、link 放在 HTML 文件结构尾部，script 标签前部<br>2、减少不必要的部分，包括 dom、style、script</p>
<h2 id="4-script-标签层次方面"><a href="#4-script-标签层次方面" class="headerlink" title="4 script 标签层次方面"></a>4 script 标签层次方面</h2><p>1、关于全局公共功能模块，提取出来公共模块化<br>2、当 js 内容不改变 dom 及 css 时，使用 async 标志<br>3、当 js 内容改变 dom 及 css 时，使用 defer 标志</p>
<h2 id="5-css-内容层次方面"><a href="#5-css-内容层次方面" class="headerlink" title="5 css 内容层次方面"></a>5 css 内容层次方面</h2><p>1、关于公共样式，提取出来公共模块化<br>2、减少样式的重复叠加</p>
<h1 id="阻塞渲染"><a href="#阻塞渲染" class="headerlink" title="阻塞渲染"></a>阻塞渲染</h1><p>现代浏览器总是并行加载资源，例如，当 HTML 解析器（HTML Parser）被脚本阻塞时，解析器虽然会停止构建 DOM，但仍会识别该脚本后面的资源，并进行预加载。</p>
<p>同时，由于下面两点：</p>
<ul>
<li><p>CSS 被视为渲染阻塞资源 (包括 JS) —— 因为 CSS 资源加载与解析和 DOM 的解析是同步构建的（可以理解为 GUI 可以同时处理 CSS 和 DOM）。因此 CSS 资源的加载和解析是不会阻塞 DOM 的解析的。但是由于渲染树依赖 DOM 和 CSSOM ，因此 CSS 资源加载与解析会阻塞渲染树的生成。</p>
</li>
<li><p>JavaScript 被认为是解释器阻塞资源，HTML 解析会被 JS 阻塞，它不仅可以读取和修改 DOM 属性，还可以读取和修改 CSSOM 属性。</p>
</li>
</ul>
<p>存在阻塞的 CSS 资源时，浏览器会延迟 JavaScript 的执行和 DOM 构建。另外：</p>
<ul>
<li><p>当浏览器遇到一个 script 标记时，DOM 构建将暂停，直至脚本完成执行。</p>
</li>
<li><p>JavaScript 可以查询和修改 DOM 与 CSSOM。</p>
</li>
<li><p>CSSOM 构建时，JavaScript 执行将暂停，直至 CSSOM 就绪(GUI 与 js 引擎互斥)。</p>
</li>
</ul>
<h1 id="js-阻塞时使用-defer-与-async-关键字"><a href="#js-阻塞时使用-defer-与-async-关键字" class="headerlink" title="js 阻塞时使用 defer 与 async 关键字"></a>js 阻塞时使用 defer 与 async 关键字</h1><p>1、当浏览器碰到 &lt;script&gt; 标签时，且标签中无任何标志 &lt;script src=”script.js”&gt;，浏览器这会立马挂起 GUI 线程，并加载执行 JS 资源。执行完毕后继续解析 DOM。(JS 阻塞后续代码)</p>
<p>2、当浏览器碰到 &lt;script&gt; 标签时，且标签中存在 async 标志 &lt;script async src=”script.js”&gt;，浏览器不会挂起 GUI 线程，而是异步加载 JS 资源。(JS 加载不阻塞 HTML 解析，但是 JS 加载完后 HTML 解析还未完成的话也会阻塞即 JS 执行时阻塞)</p>
<p>3、当浏览器碰到 &lt;script&gt; 标签时，且标签中存在 defer 标志 &lt;script defer src=”myscript.js”&gt;，浏览器这不会挂起 GUI 线程，而是异步加载 JS 资源，并等待 DOM 解析完毕后执行。JS 资源的加载不影响解析 DOM。(JS 加载与 HTML 解析并行，等待 HTML 解析完后再执行)</p>
<p><strong>如图</strong><br>蓝线代表 js 加载(读取),绿线代表 HTML 解析，红线代表 js 执行<br><img src="js阻塞关键字.jpg"/></p>
<p><strong>补充 document.createElement：</strong></p>
<p>使用 document.createElement 创建的 script 默认是异步的，示例如下。</p>
<p>所以，通过动态添加 script 标签引入 JavaScript 文件默认是不会阻塞页面的。如果想同步执行，需要将 async 属性人为设置为 false。</p>
<p>如果使用 document.createElement 创建 link 标签会怎样呢？</p>
<p>其实这只能通过试验确定，已知的是，Chrome 中已经不会阻塞渲染，Firefox、IE 在以前是阻塞的，现在会怎样目前不太清楚。</p>

    </div>

    
    
    

    <footer class="post-footer">
          <div class="followme">
  <span>欢迎关注我的其它发布渠道</span>

  <div class="social-list">

      <div class="social-item">
        <a target="_blank" class="social-link" href="/myblog/images/wechat_channel.jpg">
          <span class="icon">
            <i class="fab fa-weixin"></i>
          </span>

          <span class="label">WeChat</span>
        </a>
      </div>
  </div>
</div>

          <div class="post-tags">
              <a href="/myblog/tags/%E5%9B%9E%E6%B5%81%E9%87%8D%E7%BB%98/" rel="tag"># 回流重绘</a>
              <a href="/myblog/tags/%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B8%B2%E6%9F%93%E8%BF%87%E7%A8%8B/" rel="tag"># 浏览器渲染过程</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/myblog/2021/01/11/%E5%89%8D%E7%AB%AF%E4%BC%98%E5%8C%96/" rel="prev" title="前端优化">
                  <i class="fa fa-chevron-left"></i> 前端优化
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/myblog/2021/01/12/CSS-hack%E5%8E%9F%E7%90%86%E5%8F%8A%E5%B8%B8%E7%94%A8hack/" rel="next" title="CSS hack原理及常用hack">
                  CSS hack原理及常用hack <i class="fa fa-chevron-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






    <div class="comments" id="gitalk-container"></div>

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      const activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      const commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>
</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">匡宇迪</span>
</div>
<div class="busuanzi-count">
    <span class="post-meta-item" id="busuanzi_container_site_uv" style="display: none;">
      <span class="post-meta-item-icon">
        <i class="fa fa-user"></i>
      </span>
      <span class="site-uv" title="总访客量">
        <span id="busuanzi_value_site_uv"></span>
      </span>
    </span>
    <span class="post-meta-item" id="busuanzi_container_site_pv" style="display: none;">
      <span class="post-meta-item-icon">
        <i class="fa fa-eye"></i>
      </span>
      <span class="site-pv" title="总访问量">
        <span id="busuanzi_value_site_pv"></span>
      </span>
    </span>
</div>

    </div>
  </footer>

  
  <script src="//cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js"></script>
<script src="/myblog/js/utils.js"></script><script src="/myblog/js/motion.js"></script><script src="/myblog/js/next-boot.js"></script>

  
<script src="/myblog/js/local-search.js"></script>






  
  <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>






<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/gitalk@1.7.0/dist/gitalk.css">

<script>
NexT.utils.loadComments('#gitalk-container', () => {
  NexT.utils.getScript('//cdn.jsdelivr.net/npm/gitalk@1.7.0/dist/gitalk.min.js', () => {
    var gitalk = new Gitalk({
      clientID    : '1926f8a1c453a1121d8d',
      clientSecret: '426722f9eef7599f8ebb3220379e704ea007a5f2',
      repo        : '',
      owner       : '',
      admin       : [''],
      id          : '1af82a36045e1a0558597368b9e939c3',
        language: '',
      distractionFreeMode: true
    });
    gitalk.render('gitalk-container');
  }, window.Gitalk);
});
</script>

</body>
</html>
